using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Windows.Forms;
using FlickrNet;
using Schedulr.Configuration;
using Schedulr.Dialogs;
using Schedulr.Properties;

namespace Schedulr.Helpers
{
    internal static class FlickrServer
    {
        #region Constants & Fields

        private const string ApiKey = "c296ee49a0e9a56020a6f7ec77891d23";
        private const string SharedSecret = "341dd12cf0ed29d5";

        private static Flickr flickr;
        private static Auth authentication;

        #endregion

        #region Properties

        private static UserInfo currentUserInfo;

        public static UserInfo CurrentUserInfo
        {
            get
            {
                return currentUserInfo;
            }
            private set
            {
                if (currentUserInfo != value)
                {
                    currentUserInfo = value;
                    OnCurrentUserChanged(EventArgs.Empty);
                }
            }
        }

        #endregion

        #region Events

        public static event EventHandler<EventArgs> CurrentUserChanged;

        private static void OnCurrentUserChanged(EventArgs e)
        {
            if (CurrentUserChanged != null)
            {
                CurrentUserChanged(null, e);
            }
        }

        #endregion

        #region Login & SwitchUser

        /// <summary>
        /// Switches the current user.
        /// </summary>
        /// <returns><see langword="true"/> if the login was successful, <see langword="false"/> otherwise.</returns>
        public static bool SwitchUser()
        {
            Settings.Default.UserAuthenticationToken = null;
            authentication = null;
            return Login();
        }

        /// <summary>
        /// Logs in to Flickr.
        /// </summary>
        /// <returns><see langword="true"/> if the login was successful, <see langword="false"/> otherwise.</returns>
        public static bool Login()
        {
            if (authentication == null)
            {
                try
                {
                    Logger.Log("Logging in to Flickr.", TraceEventType.Verbose);
                    if (string.IsNullOrEmpty(Settings.Default.UserAuthenticationToken))
                    {
                        Flickr tempFlickr = new Flickr(ApiKey, SharedSecret);
                        string frob = tempFlickr.AuthGetFrob();
                        string authenticationUrl = tempFlickr.AuthCalcUrl(frob, AuthLevel.Write);
                        Logger.Log("The current user hasn't logged in before, redirecting to the Flickr site for authentication.", TraceEventType.Verbose);
                        AuthenticationDialog authenticationForm = new AuthenticationDialog(authenticationUrl);
                        DialogResult result = authenticationForm.ShowDialog(Form.ActiveForm);
                        if (result == DialogResult.Cancel)
                        {
                            Logger.Log("Could not log in to Flickr.", TraceEventType.Warning);
                            CurrentUserInfo = null;
                            return false;
                        }
                        Auth tempAuthentication = tempFlickr.AuthGetToken(frob);
                        Settings.Default.UserAuthenticationToken = tempAuthentication.Token;
                    }

                    flickr = new Flickr(ApiKey, SharedSecret, Settings.Default.UserAuthenticationToken);
                    authentication = flickr.AuthCheckToken(Settings.Default.UserAuthenticationToken);
                    Person user = flickr.PeopleGetInfo(authentication.User.UserId);
                    UserStatus status = flickr.PeopleGetUploadStatus();
                    string userStatus = string.Format(CultureInfo.CurrentCulture, "You've uploaded {0} of your {1} limit this month ({2}).{3}The maximum file size you are allowed to upload is {4}.", Converter.GetBytesAsString(status.BandwidthUsed), Converter.GetBytesAsString(status.BandwidthMax), status.PercentageUsed.ToString("p0", CultureInfo.CurrentCulture), Environment.NewLine, Converter.GetBytesAsString(status.FilesizeMax));
                    MemberGroupInfo[] groups = flickr.GroupPoolGetGroups();
                    Photoset[] sets = flickr.PhotosetsGetList().PhotosetCollection;
                    CurrentUserInfo = new UserInfo(authentication.User.Username, user.PhotosUrl, user.BuddyIconUrl.ToString(), userStatus, status.IsPro, status.FilesizeMax, groups, sets);
                    Logger.Log(string.Format(CultureInfo.CurrentCulture, "Logged in to Flickr as {0}.", CurrentUserInfo.UserName), TraceEventType.Information);
                }
                catch (FlickrApiException exc)
                {
                    if (exc.Code == 108)
                    {
                        Logger.Log("Flickr Schedulr could not authenticate the current user with Flickr. Restart the application to try again.");
                        MessageBox.Show("You have not authorized Flickr Schedulr to access your account yet. Please start the application again and finish the authentication process.", "Not authenticated", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        CurrentUserInfo = null;
                        return false;
                    }
                    else if (exc.Code == 98)
                    {
                        Logger.Log("The user's authentication token was invalid. Restart the application to try again.");
                        MessageBox.Show("Your authentication token has become invalid. This might be because you revoked the permission for Flickr Schedulr to access your account. Please start the application again and restart the authentication process.", "Not authenticated", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        Settings.Default.UserAuthenticationToken = null;
                        CurrentUserInfo = null;
                        return false;
                    }
                    else
                    {
                        throw;
                    }
                }
                catch (WebException webex)
                {
                    Logger.Log("Could not log in to Flickr: " + webex.Message, webex);
                    CurrentUserInfo = null;
                    return false;
                }
            }
            return true;
        }

        #endregion

        #region Upload Picture

        /// <summary>
        /// Uploads the next picture in the queue.
        /// </summary>
        public static void UploadNextPictureBatchInQueue()
        {
            SchedulrConfiguration config = SchedulrConfiguration.Instance;
            if (config.QueuedPictures.Count > 0)
            {
                Logger.Log("Uploading next picture batch in queue.", TraceEventType.Information);
                bool uploadNext = true;
                while (uploadNext && config.QueuedPictures.Count > 0)
                {
                    // Upload the picture.
                    Picture pictureToUpload = config.QueuedPictures[0];
                    uploadNext = pictureToUpload.UploadWithNext;
                    UploadPicture(pictureToUpload);
                    Logger.Log("Uploaded picture and updated queue.", TraceEventType.Information);
                }
                Logger.Log("Finished uploading picture batch in queue.", TraceEventType.Information);
            }
            else
            {
                Logger.Log("There are no pictures to upload in the queue.", TraceEventType.Information);
            }
        }

        /// <summary>
        /// Uploads the given pictures.
        /// </summary>
        /// <param name="picture">The pictures to upload.</param>
        public static void UploadPictures(IList<Picture> pictures)
        {
            foreach (Picture picture in pictures)
            {
                UploadPicture(picture);
            }
        }

        /// <summary>
        /// Uploads the given picture.
        /// </summary>
        /// <param name="picture">The picture to upload.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private static void UploadPicture(Picture picture)
        {
            var loggedIn = Login();
            if (!loggedIn)
            {
                Logger.Log("Cannot upload the requested picture because login failed.", TraceEventType.Error);
            }
            else if (!File.Exists(picture.FileName))
            {
                Logger.Log("The picture to be uploaded does not exist on disk: " + picture.FileName, TraceEventType.Error);
            }
            else
            {
                Logger.Log(string.Format(CultureInfo.CurrentCulture, "Uploading picture \"{0}\".", picture.FileName), TraceEventType.Information);
                string pictureId = flickr.UploadPicture(picture.FileName, picture.Title, picture.Description, picture.Tags, picture.IsPublic, picture.IsFamily, picture.IsFriend);

                foreach (string setId in picture.SetIds)
                {
                    try
                    {
                        Logger.Log("Adding picture to photoset: " + setId, TraceEventType.Information);
                        flickr.PhotosetsAddPhoto(setId, pictureId);
                    }
                    catch (Exception exc)
                    {
                        Logger.Log("Could not add picture to photoset", exc);
                    }
                }

                foreach (string groupId in picture.GroupIds)
                {
                    try
                    {
                        Logger.Log("Adding picture to group: " + groupId, TraceEventType.Information);
                        flickr.GroupPoolAdd(pictureId, groupId);
                    }
                    catch (Exception exc)
                    {
                        Logger.Log("Could not add picture to group", exc);
                    }
                }

                if (Settings.Default.DeleteFileAfterUpload)
                {
                    File.Delete(picture.FileName);
                }

                // Move it from the queued pictures to the uploaded pictures.
                SchedulrConfiguration config = SchedulrConfiguration.Instance;
                if (config.QueuedPictures.Contains(picture))
                {
                    config.QueuedPictures.Remove(picture);
                    config.UploadedPictures.Insert(0, new UploadedPicture(picture));
                }
            }
        }

        #endregion
    }
}